Skip to main content

News Provider

Provider that supplies data about events for the selected trading instrument (corporate actions, corporate calendar, earning reports) and news.

import Combine
/// A protocol for providing news data related to a specific financial instrument.
///
/// The `NewsProvider` protocol defines the required functionalities for any provider that supplies
/// news data, corporate actions, conference call information, and earnings data for a presented symbol.
/// Conforming types must implement the necessary publishers to push relevant data updates.
///
/// This protocol is useful for applications that need to display timely news and corporate information
/// to users based on the selected financial instrument.
public protocol NewsProvider {
/// A publisher that emits an array of `NewsItem` instances related to the presented symbol.
///
/// The publisher sends updates as a result of fetching news data, providing either
/// a success case containing an array of news items or an error case if the request fails.
var newsPublisher: AnyPublisher<Result<[DXChart.NewsItem], Error>, Never> { get }
/// A publisher that emits an array of `CorporateAction` instances.
///
/// This publisher sends updates on corporate actions (e.g., stock splits, dividends) relevant to
/// the presented symbol, similar to the `newsPublisher` in structure and functionality.
var corporateActionPublisher: AnyPublisher<Result<[DXChart.CorporateAction], Error>, Never> { get }
/// A publisher that emits an array of `Earning` instances related to conference calls.
///
/// This publisher provides information about upcoming and past conference calls for the
/// presented symbol, allowing consumers to stay informed about earnings announcements.
var conferenceCallPublisher: AnyPublisher<Result<[DXChart.Earning], Error>, Never> { get }
/// A publisher that emits an array of `Earning` instances related to earnings reports.
///
/// This publisher sends updates on earnings data for the symbol, helping consumers track performance
/// and financial metrics associated with the instrument.
var earningsPublisher: AnyPublisher<Result<[DXChart.Earning], Error>, Never> { get }
/// Updates the data for the specified financial instrument symbol.
///
/// - Parameter symbol: The symbol for which to fetch and update news data.
/// This method should be called whenever the user selects a different symbol to display
/// its associated news and corporate actions.
func updateData(for symbol: String)
}

Events set on the chart look as follows:

events_on_chart

Default example

If you haven't implemented a provider for News, Earnings, Corporate Actions, and Conference Calls earlier and are using DXFeedFramework to retrieve data, you can use our default provider. To work with it and DXFeedFramework, you need credentials such as a username and password, which should be placed in the placeholders within the Headers section of the Network Provider. Please contact your DXFeed sales manager or visit https://dxfeed.com/contact-sales/ for assistance.

Default implementation code:

import Combine
import DXChart
final class DXFeedNewsProvider: DXChart.NewsProvider {
private let _newsPublisher = PassthroughSubject<Result<[DXChart.NewsItem], Error>, Never>()
private let _corporateActionPublisher = PassthroughSubject<Result<[DXChart.CorporateAction], Error>, Never>()
private let _conferenceCallPublisher = PassthroughSubject<Result<[DXChart.Earning], Error>, Never>()
private let _earningsPublisher = PassthroughSubject<Result<[DXChart.Earning], Error>, Never>()
private let networkManager = NetworkManager()
var newsPublisher: AnyPublisher<Result<[DXChart.NewsItem], Error>, Never> {
_newsPublisher.eraseToAnyPublisher()
}
var corporateActionPublisher: AnyPublisher<Result<[DXChart.CorporateAction], Error>, Never> {
_corporateActionPublisher.eraseToAnyPublisher()
}
var conferenceCallPublisher: AnyPublisher<Result<[DXChart.Earning], Error>, Never> {
_conferenceCallPublisher.eraseToAnyPublisher()
}
var earningsPublisher: AnyPublisher<Result<[DXChart.Earning], Error>, Never> {
_earningsPublisher.eraseToAnyPublisher()
}
init(currentSymbol: String) {
updateData(for: currentSymbol)
}
func updateData(for symbol: String) {
sendNewsListRequest()
getCorporateAction()
getConferenceCall()
getEarnings()
}
private func sendNewsListRequest() {
networkManager.sendNewsListRequest { [weak self] result in
guard let self else { return }
switch result {
case .success(let news):
self._newsPublisher.send(.success(news))
case .failure(let error):
switch error {
case ServerError.tooManyRequests:
DispatchQueue.global().asyncAfter(deadline: .now() + 1, execute: { [weak self] in
self?.sendNewsListRequest()
})
default:
self._newsPublisher.send(.failure(error))
}
}
}
}
func getCorporateAction() {
let completion: (Result<[DXChart.CorporateAction], Error>) -> Void = { [weak self] result in
guard let self else {
self?._corporateActionPublisher.send(Result.failure(ServerError.noData))
return
}
switch result {
case .success(let response):
_corporateActionPublisher.send(.success(response))
case .failure(let error):
switch error {
case ServerError.tooManyRequests:
DispatchQueue.global().asyncAfter(deadline: .now() + 1, execute: { [weak self] in
self?.getConferenceCall()
})
default:
_corporateActionPublisher.send(.failure(error))
}
}
}
networkManager.sendCorporateActionRequest(completion: completion)
}
private func getConferenceCall() {
let completion: (Result<[DXChart.Earning], Error>) -> Void = { [weak self] result in
guard let self else {
self?._conferenceCallPublisher.send(Result.failure(ServerError.noData))
return
}
switch result {
case .success(let response):
_conferenceCallPublisher.send(.success(response))
case .failure(let error):
switch error {
case ServerError.tooManyRequests:
DispatchQueue.global().asyncAfter(deadline: .now() + 1, execute: { [weak self] in
self?.getConferenceCall()
})
default:
_conferenceCallPublisher.send(.failure(error))
}
}
}
networkManager.sendConferenceCallRequest(completion: completion)
}
private func getEarnings() {
let completion: (Result<[DXChart.Earning], Error>) -> Void = { [weak self] result in
guard let self else {
self?._earningsPublisher.send(Result.failure(ServerError.noData))
return
}
switch result {
case .success(let response):
_earningsPublisher.send(.success(response))
case .failure(let error):
switch error {
case ServerError.tooManyRequests:
DispatchQueue.global().asyncAfter(deadline: .now() + 1, execute: { [weak self] in
self?.getEarnings()
})
default:
_earningsPublisher.send(.failure(error))
}
}
}
networkManager.sendEarningsRequest(completion: completion)
}
}

This example include next data types:

Network Manager

import DXChart
// Models
typealias ServerError = DXChart.ServerError
struct NewsResponse: Codable {
let news: [DXChart.NewsItem]
}
// Network manager
final class NetworkManager {
private let decoder = JSONDecoder()
func sendNewsListRequest(completion: @escaping (Result<[DXChart.NewsItem], Error>) -> Void) {
sendRequest(request: .getNewsList, parse: parseNewsList) { response in
switch response {
case .success(let newsResponse):
completion(.success(newsResponse.news))
case .failure(let error):
completion(.failure(error))
}
}
}
func sendCorporateActionRequest(completion: @escaping (Result<[DXChart.CorporateAction], Error>) -> Void) {
sendRequest(request: .getCorporateAction, parse: parseCorporateAction, completion: completion)
}
func sendConferenceCallRequest(completion: @escaping (Result<[DXChart.Earning], Error>) -> Void) {
sendRequest(request: .getCorporateCalendar, parse: parseConferenceCall, completion: completion)
}
func sendEarningsRequest(completion: @escaping (Result<[DXChart.Earning], Error>) -> Void) {
sendRequest(request: .getEarningReport, parse: parseEarning, completion: completion)
}
func sendRequest<T: Decodable>(request: NetworkRequest, parse: @escaping (Data) -> T?, completion: @escaping (Result<T, Error>) -> Void) {
do {
let request = try request.makeURLRequest()
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
if let error {
debugPrint("Error: (error)")
completion(.failure(ServerError.unknown))
return
}
guard let httpResponse = response as? HTTPURLResponse else {
completion(.failure(ServerError.unknown))
return
}
switch httpResponse.statusCode {
case 200...299:
guard let data = data else {
completion(.failure(ServerError.noData))
return
}
guard let parsedData = parse(data) else {
completion(.failure(ServerError.parseError))
return
}
completion(.success(parsedData))
case 429:
completion(.failure(ServerError.tooManyRequests))
case 401:
completion(.failure(ServerError.notAuthorized))
default:
completion(.failure(ServerError.unknown))
}
}
task.resume()
} catch {
debugPrint("Error creating request: (error)")
completion(.failure(ServerError.unknown))
}
}
private func parseNewsList(data: Data) -> NewsResponse? {
do {
let newsResponse = try decoder.decode(NewsResponse.self, from: data)
return newsResponse
} catch {
debugPrint("Error decoding JSON news: (error)")
return nil
}
}
private func parseCorporateAction(data: Data) -> [CorporateAction]? {
do {
let corporateAction = try decoder.decode([CorporateAction].self, from: data)
return corporateAction
} catch {
debugPrint("Error decoding JSON CorporateAction: (error)")
return nil
}
}
private func parseConferenceCall(data: Data) -> [Earning]? {
do {
let conferenceCalls = try decoder.decode([Earning].self, from: data)
let calls = conferenceCalls.filter { DXChart.AnnounceType(rawValue: $0.announceType) == .conferenceCall }
return calls
} catch {
debugPrint("Error decoding JSON ConferenceCall: (error)")
return nil
}
}
private func parseEarning(data: Data) -> [Earning]? {
do {
let earnings = try decoder.decode([Earning].self, from: data)
let earningsReport = earnings.filter { DXChart.AnnounceType(rawValue: $0.announceType) == .earning }
return earningsReport
} catch {
debugPrint("Error decoding JSON Earning: (error)")
return nil
}
}
}

NetworkRequest

import DXChart
enum NetworkRequest {
case getNewsList
case getCorporateAction
case getCorporateCalendar
case getEarningReport
}
extension NetworkRequest: DXChart.NetworkRequest {
public var baseURL: String {
switch self {
case .getNewsList:
"https://demo.dxfeed.com/"
case .getCorporateAction,
.getCorporateCalendar,
.getEarningReport:
"https://tools.dxfeed.com/"
}
}
var path: String {
let instrument = DXCUserDefaultsManager.shared.instrument
return switch self {
case .getNewsList:
"news-demo?symbol=(instrument.symbol)"
case .getCorporateAction:
"fs/v0.1/corporate-action/symbol/(instrument.symbol)"
case .getEarningReport, .getCorporateCalendar:
"fs/v0.1/earning/symbol/(instrument.symbol)"
}
}
var method: String {
"GET"
}
var params: [String: Any]? {
nil
}
var headers: [String: String]? {
switch self {
case .getNewsList:
let credentials = "<#provided login#>:<#provided password#>"
let encodedCredentials = Data(credentials.utf8).base64EncodedString()
return ["Authorization": "Basic (encodedCredentials)"]
case .getCorporateAction, .getCorporateCalendar, .getEarningReport:
let credentials = "<#provided login#>:<#provided password#>"
let encodedCredentials = Data(credentials.utf8).base64EncodedString()
return ["Authorization": "Basic (encodedCredentials)"]
}
}
}

NewsItem

/// A struct representing a news item that can be displayed in a chart or other UI component.
///
/// This model contains key information about a news event, including its title, publication time, and a URL to the full content.
/// It is designed to be used in conjunction with components that display financial data, like charts, where the title and
/// timestamp of the news are shown and, optionally, a clickable link to the full content is provided.
public struct NewsItem: Codable {
/// The URL to the full news content.
///
/// If "body" is "nil", the news item will not be clickable, meaning that the item serves only as an informational piece.
/// If "body" is not "nil", the news item can be tapped or clicked on to open the associated URL in a web browser, allowing
/// users to view the full news article or content.
///
/// - Example: "https://www.example.com/news/12345"
public let body: URL?
/// The timestamp of when the news item was published, in milliseconds since the Unix epoch (1970-01-01T00:00:00Z).
///
/// This value is useful for sorting or displaying the news items in chronological order.
///
/// - Example: "1635964800000" (represents a time in November 2021)
public let time: Int64?
/// The title of the news item that will be displayed in the chart or list.
///
/// This string provides a brief summary or headline of the news event, which is meant to catch the user's attention.
/// It typically includes the company name, ticker symbol, and a short description of the news event.
///
/// - Example: '"Apple Inc. (AAPL) Q4 2021 Earnings Call Transcript"'
public let title: String
/// Initializes a "NewsItem" instance.
///
/// - Parameters:
/// - body: A URL to the full news content. If "nil", the news will not be clickable.
/// - time: The timestamp of the news in milliseconds since the Unix epoch.
/// - title: The headline or title of the news.
init(body: URL?, time: Int64?, title: String) {
self.body = body
self.time = time
self.title = title
}

Corporate Action

/// A struct representing a corporate action event such as earnings, dividends, stock splits, or conference calls.
///
/// This structure contains all relevant data about a corporate action event, including the type of action, the event time,
/// any adjustment value (e.g., for dividends), and optional additional information such as a split (via "Ext") and the event point on a chart.
public struct CorporateAction: EventsData {
/// The timestamp of the corporate action event in milliseconds since the Unix epoch (1970-01-01T00:00:00Z).
public let eventTime: Int64
/// The type of the corporate action event.
///
/// Examples include "EARNING", "DIVIDEND", "CONFERENCE_CALL", "STOCK_SPLIT".
public let announceType: String
/// The date of the corporate action event in "YYYYMMDD" format. (example: 20150205)
///
/// This integer represents the ex-date of the action, which is typically important for dividend and stock split events.
public let ymd: Int
/// The gross value associated with the corporate action event, typically for dividends.
///
/// This is an optional value used mainly in the context of dividend events to represent the dividend value.
public let adjustmentValue: Float?
/// Additional information about the corporate action, particularly for stock splits.
///
/// This optional field holds data related to stock splits, detailing the stock's value before and after the split (via the "Ext" struct).
public let ext: Ext?
/// An optional value used to recognize the event's point location on a chart.
///
/// This can be used to visually place the corporate action event at a specific point on a chart, indicating the time and value.
public let point: EventPoint?
}

Corporate actions and other events should comply with the EventsData protocol:

/// A protocol for data types that represent events, such as corporate actions.
///
/// Types conforming to "EventsData" provide key information about an event, including its timestamp, type, and point on a chart.
public protocol EventsData: Codable {
/// The timestamp of the event in milliseconds since the Unix epoch (1970-01-01T00:00:00Z).
var eventTime: Int64 { get }
/// The announceType of the event (e.g., "EARNING", "DIVIDEND", "STOCK_SPLIT").
var announceType: String { get }
/// The point location of the event on a chart, if applicable.
var point: EventPoint? { get }
/// The date of the event in "YYYYMMDD" format.
var ymd: Int { get }
}

Corporate Calendar

Use Earning structure now see it below.

Earning Report

/// A struct representing an earnings event data point, containing key information about an earnings announcement.
///
/// The "Earning" structure holds details such as the name of the earnings report, the event's date and time, the type of earnings announcement,
/// as well as optional data like earnings per share (EPS) and estimated EPS values.
public struct Earning: EventsData, Codable {
/// The name of the earnings announcement.
///
/// This is a string that describes the earnings event, such as the company name and the relevant quarter.
public let name: String
/// The date of the earnings event in "YYYYMMDD" format. Example: 20150205
///
/// This integer represents the specific date of the earnings announcement.
public let ymd: Int
/// The timestamp of the earnings event in milliseconds since the Unix epoch (1970-01-01T00:00:00Z).
///
/// This value represents the time the earnings event occurred or was announced. Example: 1643416910176
public let eventTime: Int64
/// An optional point that indicates the location of this earnings event on a chart.
///
/// The "EventPoint" can be used to visually place the earnings event on a chart, typically representing both time and value dimensions.
public let point: EventPoint?
/// The type of the earnings announcement.
///
/// This string typically describes the type of earnings event, such as "EARNING", "DIVIDEND", "CONFERENCE_CALL", "STOCK_SPLIT".
public let announceType: String
/// The reference period for the earnings report (e.g., "Q4 2021").
///
/// This string describes the fiscal period or quarter to which the earnings announcement applies.
let referencePeriod: String
/// The actual earnings per share (EPS) reported during the earnings event.
///
/// This is an optional value representing the EPS as reported by the company.
let eps: Double?
/// The estimated earnings per share (EPS) before the actual earnings report.
///
/// This is an optional value that represents the expected EPS, typically predicted by analysts before the actual earnings report is released.
let estimatedEPS: Double?
}